﻿using System;
using Firefly.ExtensionMethods;
using Firefly.Framework.Collections;
using Firefly.Framework.Collections.Spatial;
using Firefly.Framework.Components.Cameras;
using Firefly.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Firefly.Framework.Components.Water
{
    public abstract class Terrain<T> : QuadTree<T>, IQuadWater where T : IQuadTreeNode
    {
        public Terrain(Game game)
            : base()
        {
            Game = game;
            Enabled = true;
            UpdateOrder = 0;
            Visible = true;
            DrawOrder = 0;
            Children = new ComponentCollection();
        }

        #region ITerrain Members
        
        public virtual WaterShader Shader { get; set; }
        public virtual int Width { get; protected set; }
        public virtual int Height { get; protected set; }
        public virtual float LodHeightImpact { get; set; }
        public virtual float LevelOfDetail { get; set; }

        public abstract float GetHeight(float x, float z);

        public abstract float GetHeightEx(float x, float z);

        public abstract float GetLodHeight(float x, float z);

        public abstract Vector3 GetNormal(float x, float z);

        public abstract Vector3 GetNormalEx(float x, float z);

        #endregion
        
        #region IComponent Members

        public IComponent Parent { get; set; }
        public ComponentCollection Children { get; protected set; }

        #endregion

        #region IUpdateableComponent Members

        public Game Game { get; protected set; }

        #endregion

        #region IUpdateable Members

        public event EventHandler EnabledChanged;
        public event EventHandler UpdateOrderChanged;

        private bool _enabled;
        private int _updateOrder;

        public bool Enabled
        {
            get { return _enabled; }
            set
            {
                if (value != _enabled)
                {
                    _enabled = value;
                    NotifyEnabledChanged();
                }
            }
        }
        public int UpdateOrder
        {
            get { return _updateOrder; }
            set
            {
                if (value != _updateOrder)
                {
                    _updateOrder = value;
                    NotifyUpdateOrderChanged();
                }
            }
        }

        protected void NotifyEnabledChanged()
        {
            EventHandler handler = EnabledChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        protected void NotifyUpdateOrderChanged()
        {
            EventHandler handler = UpdateOrderChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        public abstract void Update(GameTime gameTime);

        #endregion

        #region IGameComponentEx Members

        public bool Initialized { get; protected set; }

        public void Uninitialize()
        {
        }

        #endregion

        #region IGameComponent Members

        public void Initialize()
        {
            Initialized = true;
        }

        #endregion

        #region IDrawable Members

        public event EventHandler DrawOrderChanged;
        public event EventHandler VisibleChanged;

        private int _drawOrder;
        private bool _visible;

        public int DrawOrder
        {
            get { return _drawOrder; }
            set
            {
                if (value != _drawOrder)
                {
                    _drawOrder = value;
                    NotifyDrawOrderChanged();
                }
            }
        }

        public bool Visible
        {
            get { return _visible; }
            set
            {
                if (value != _visible)
                {
                    _visible = value;
                    NotifyVisibleChanged();
                }
            }
        }

        protected void NotifyDrawOrderChanged()
        {
            EventHandler handler = DrawOrderChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        protected void NotifyVisibleChanged()
        {
            EventHandler handler = VisibleChanged;

            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        public abstract void Draw(GameTime gameTime);

        #endregion

        #region IHasContent Members

        public abstract void LoadContent();

        public abstract void UnloadContent();

        #endregion

        #region ISpatial3D Members

        public Vector3 Position { get; set; }
        public Vector3 Offset { get; set; }
        public Vector3 EulerRotation
        {
            get { return Rotation.ToVector3(); }
            set { Rotation = ((Vector3)value).ToRotation(); }
        }
        public Matrix Rotation { get; set; }
        public Vector3 Scale { get; set; }
        public BoundingBox BoundingBox
        {
            get { return new BoundingBox(Position - (Scale / 2) + Offset, Position + (Scale / 2) + Offset); }
        }

        public void SetPosition(float x, float y, float z)
        {
            Position = new Vector3(x, y, z);
        }

        public void SetOffset(float x, float y, float z)
        {
            Offset = new Vector3(x, y, z);
        }

        #endregion

        #region IHasCamera

        public ICamera Camera { get; set; }

        #endregion

        #region IRefractable Members

        public void DrawRefraction(GameTime gameTime)
        {
            if (Visible)
            {
                float originalLod = LevelOfDetail;
                LevelOfDetail = 1.0f;
                Draw(gameTime);
                LevelOfDetail = originalLod;
            }
        }

        #endregion

        #region IDisposableEx Members

        public bool Disposed { get; protected set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected abstract void Dispose(bool disposing);

        #endregion
    }
}
